#include "xsltransformer.h"
#include "../AnalyseurXML/src/document.h"
#include "../AnalyseurXML/src/element.h"
#include "../AnalyseurXML/src/attribute.h"
#include "../AnalyseurXML/src/attr_node.h"

using namespace std;

XSLTransformer::XSLTransformer(Document* xslDocument) : mXSLDocument(xslDocument) {
}


Document* XSLTransformer::transformXML(Document* xmlDocument) {
	Document* newDoc = new Document;
	Node* newRoot = transformNode(xmlDocument->getRoot());
	XmlElement* newRootElem = XmlElement::getElement(newRoot);
	if(newRootElem != NULL)
	{
		newDoc->setRoot(newRootElem);
		return newDoc;
	}
	else
		return NULL;
}


Node* XSLTransformer::transformNode(XmlElement* node){

	list<Node*>* children;
	list<Node*>::const_iterator iterator;

	XmlElement* templateNode = NULL; // node xsl:template du document xsl avec match du tag de node
	
	children = mXSLDocument->getRoot()->getChildren();


	for (iterator = children->begin(); iterator != children->end(); ++iterator) {
		 // si on trouve un template pour le noeud :
		XmlElement* templateElem = XmlElement::getElement(*iterator);
		if(templateElem != NULL) // s'il s'agit bien d'un noeud de type "Element"
		{
			if((templateElem->getTag() == "template") && (templateElem->getAttributeValue("match") == node->getTag()))
			{
				templateNode = templateElem; // récupération du noeud "xsl:template"
				// todo : exit loop
			}
		}
	}




	if(templateNode == NULL) // pas de template trouvé pour node :
	{
		XmlElement* newNode = new XmlElement(*node); // copie du node tel quel
		
		//pour chaque fils, appeler transform
		list<Node*> children2 = list<Node*>(*(newNode->getChildren()));

		for (iterator = children2.begin(); iterator != children2.end(); ++iterator) {
			XmlElement* childElem = XmlElement::getElement(*iterator);
			if(childElem != NULL) // si le fils est de type Element
			{
				Node* newChild = transformNode(childElem);
				newNode->removeChild(*iterator); // suppression de l'ancien fils

				newNode->addChild(newChild); // ajout du nouveau fils
			}
		}

		return newNode;
	}

	// sinon, si le templateNode n'est pas NULL :

	Node* firstChild = templateNode->getChildren()->front(); // premier enfant du xsl:template sera le noeud retourné

	XmlElement* firstChildElem = XmlElement::getElement(firstChild);

	if(firstChildElem != NULL)
	{
		XmlElement* newNode = new XmlElement(*firstChildElem);
		transformTemplate(newNode, node); // appel recursif pour transformer l'arbre du template
		return newNode;
	}
	else
		return firstChild->clone();

	
}

void XSLTransformer::transformTemplate(XmlElement* templateNode, XmlElement* originalNode){

	list<Node*> children = list<Node*>(*(templateNode->getChildren()));
	list<Node*>::const_iterator iterator;

	// pour chaque fils de templateNode :
	for (iterator = children.begin(); iterator != children.end(); ++iterator) {

		XmlElement* templateChild = XmlElement::getElement(*iterator);

		if(templateChild != NULL) // si le noeud est de type Element
		{

			if(templateChild->getTag() == "apply-templates")
			{
				list<Node*>*originalNodeChildren = originalNode->getChildren();
				list<Node*>::const_iterator iter;

				// pour chaque fils du noeud original, on recherche des templates :
				for(iter = originalNodeChildren->begin(); iter != originalNodeChildren->end(); ++iter)
				{
					XmlElement* elem = XmlElement::getElement(*iter);
					if(elem != NULL) // si le fils du noeud original est de type "Element"
					{
						Node* newChild = transformNode(elem);
						templateNode->addChild(newChild); // ajout du nouveau noeud au parent de xsl:apply-templates : aka templateNode
					}
				}

				templateNode->removeChild(templateChild); // suppression de xsl:apply-templates

			}
			else if(templateChild->getTag() == "value-of")
			{
				// ajoute le contenu pointé par le value-of

				templateChild->getParent()->removeChild(templateChild); // suppression de xsl:value-of
			}
			else // si le noeud est un noeud standard, on propage la transformation dans les fils (c'est donc les fils du fils)
			{
				XmlElement* childElem = XmlElement::getElement(*iterator);
				if(childElem != NULL) // si le fils est de type "Element", il a lui aussi des fils
				{
					list<Node*>*childChildren = childElem->getChildren();
					list<Node*>::const_iterator iter;
					for(iter = childChildren->begin(); iter != childChildren->end(); ++iter)
					{
						XmlElement* elem = XmlElement::getElement(*iter);
						if(elem != NULL) // si le fils du fils est de type "Element", on propage
							transformTemplate(elem, originalNode);
					}
				}
			}

		}

	}

}
